﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Loki.Commands;
using Loki.Utils;

namespace Loki.UI
{
    /// <summary>
    /// Base class for container view models.
    /// </summary>
    /// <typeparam name="TModel">The type of the application model.</typeparam>
    public abstract class ContainerViewModel : ViewModel, IContainerViewModel
    {
        private List<WeakReference<IEnumerable<IViewModel>>> _ChildModels;

        private BindableCollection<IViewModel> _SubModels;

        public ContainerViewModel()
        {
            _ChildModels = new List<WeakReference<IEnumerable<IViewModel>>>();
            _SubModels = CreateViewModelCollection();
        }

        /// <summary>
        /// Gets the sub models.
        /// </summary>
        /// <value>The sub models.</value>
        public IObservableCollection<IViewModel> SubModels
        {
            get { return this._SubModels; }
        }

        public override void AcceptChanges()
        {
            foreach (var L_SubModel in _SubModels)
            {
                L_SubModel.AcceptChanges();
            }

            base.AcceptChanges();
        }

        public override void BeginInit()
        {
            foreach (var L_SubModel in _SubModels)
            {
                L_SubModel.BeginInit();
            }
        }

        public override void EndInit(bool P_IsStable)
        {
            foreach (var L_SubModel in _SubModels)
            {
                L_SubModel.EndInit(P_IsStable);
            }

            base.EndInit(P_IsStable);
        }

        public override bool Validate(ValidationMode P_Mode)
        {
            bool L_Return = true;
            foreach (var L_VM in _SubModels)
            {
                L_VM.IsValid = true;
                L_VM.Validate(P_Mode);
                L_Return &= L_VM.IsValid;
            }

            return L_Return;
        }

        protected TViewModel ActivateOrAdd<TViewModel>()
            where TViewModel : ViewModel
        {
            return ActivateOrAdd<TViewModel>(null, null);
        }

        protected TViewModel ActivateOrAdd<TViewModel>(Func<TViewModel, bool> P_Selector)
            where TViewModel : ViewModel
        {
            return ActivateOrAdd<TViewModel>(P_Selector, null);
        }

        protected TViewModel ActivateOrAdd<TViewModel>(Action<TViewModel> P_Initializer)
            where TViewModel : ViewModel
        {
            return ActivateOrAdd<TViewModel>(null, P_Initializer);
        }

        protected TViewModel ActivateOrAdd<TViewModel>(Func<TViewModel, bool> P_Selector, Action<TViewModel> P_Initializer)
            where TViewModel : ViewModel
        {
            // search
            TViewModel L_Model = null;
            if (P_Selector != null)
            {
                L_Model = _SubModels.OfType<TViewModel>().FirstOrDefault(x => P_Selector(x));
            }
            else
            {
                L_Model = _SubModels.OfType<TViewModel>().FirstOrDefault();
            }

            // init
            if (L_Model == null)
            {
                L_Model = AddNew<TViewModel>(P_Initializer);
            }
            else
            {
                L_Model.Activate.Execute(null);
            }

            Log.Info("submodel activate");

            return L_Model;
        }

        protected TViewModel AddNew<TViewModel>() where TViewModel : ViewModel
        {
            return AddNew<TViewModel>(null);
        }

        protected TViewModel AddNew<TViewModel>(Action<TViewModel> P_Initializer) where TViewModel : ViewModel
        {
            TViewModel L_NewModel = ToolKit.Engine.UIContext.Get<TViewModel>();

            AddSubModel(L_NewModel, P_Initializer);

            L_NewModel.Activate.Execute(null);

            return L_NewModel;
        }

        protected BindableCollection<IViewModel> CreateViewModelCollection()
        {
            var L_SubModels = CreateTrackedCollection<IViewModel>();
            L_SubModels.IsTrackingRemovedItems = false;
            L_SubModels.CollectionChanged += SubModels_CollectionChanged;
            _ChildModels.Add(WeakReference<IEnumerable<IViewModel>>.Create(L_SubModels));

            return L_SubModels;
        }

        protected void DoOnChildVMs(Action<IViewModel> P_Action)
        {
            var L_DeadReferences = new List<WeakReference<IEnumerable<IViewModel>>>();

            foreach (var L_SubCollectionReference in _ChildModels)
            {
                if (L_SubCollectionReference.IsAlive)
                {
                    var L_SubCollection = L_SubCollectionReference.Target;
                    if (L_SubCollection != null)
                    {
                        foreach (var L_VM in L_SubCollection)
                        {
                            P_Action(L_VM);
                        }
                    }
                    else
                    {
                        L_DeadReferences.Add(L_SubCollectionReference);
                    }
                }
                else
                {
                    L_DeadReferences.Add(L_SubCollectionReference);
                }
            }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            DoOnChildVMs(x => x.Initialize());
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            DoOnChildVMs(x => x.LoadData());
        }

        protected override void OnStart(EventArgs e)
        {
            base.OnStart(e);
            DoOnChildVMs(x =>
            {
                if (x.IsActive)
                {
                    x.StartInteraction();
                }
            });
        }

        protected override void ProcessActivate()
        {
            if (ActiveDocument != null && !ActiveDocument.IsActive)
            {
                ActiveDocument.Activate.Execute(null);
            }

            base.ProcessActivate();
        }

        protected override void ProcessDesactivate()
        {
            if (ActiveDocument != null && ActiveDocument.IsActive)
            {
                ActiveDocument.Desactivate.Execute(null);
            }

            base.ProcessDesactivate();
        }

        protected override void SubCollection_ItemChanged<T>(object P_Sender, ItemChangedEventArgs<T> e)
        {
            if (e.Item is ISubViewModel)
            {
                base.SubCollection_ItemChanged(P_Sender, e);
            }
        }

        private void AddSubModel<TViewModel>(TViewModel P_Model, Action<TViewModel> P_Initializer) where TViewModel : ViewModel
        {
            P_Model.Initialize();

            if (P_Initializer != null)
            {
                P_Initializer(P_Model);
            }

            Commands.Handle(P_Model.Close, (s, e) => SubModel_Close(P_Model, e));
            Commands.Handle(P_Model.Activate, (s, e) => SubModel_Activate(P_Model, e));
            _SubModels.Add(P_Model);
        }

        private void SubModel_Activate(IViewModel P_Model, CommandEventArgs P_Args)
        {
            if (!IsActive)
            {
                this.Activate.Execute(null);
            }

            foreach (var L_SubModel in _SubModels)
            {
                if (P_Model != L_SubModel)
                {
                    L_SubModel.Desactivate.Execute(null);
                }
            }

            OnSubModelActivated(new ViewModelEventArgs(P_Model));
            ActiveDocument = P_Model;
        }

        private void SubModel_Close(IViewModel P_Model, CommandEventArgs P_Args)
        {
            Commands.UnHandle(P_Model.Close);
            Commands.UnHandle(P_Model.Activate);
            Log.Debug("submodel before remove");
            P_Model.DoCleanup();

            int L_OldIndex = _SubModels.IndexOf(P_Model);

            _SubModels.Remove(P_Model);

            if (ActiveDocument == P_Model)
            {
                ActiveDocument = null;
            }

            if (_SubModels.Count > 0)
            {
                if (L_OldIndex > 0)
                {
                    _SubModels[L_OldIndex - 1].Activate.Execute(null);
                }
                else
                {
                    _SubModels[0].Activate.Execute(null);
                }
            }

            Log.Debug("submodel removed");
        }

        private void SubModels_CollectionChanged(object P_Sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (var L_SubModel in e.NewItems)
                {
                    ISubViewModel L_Sub = L_SubModel as ISubViewModel;
                    if (L_Sub != null)
                    {
                        L_Sub.Parent = this;
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                IEnumerable<IViewModel> L_Sender = P_Sender as IEnumerable<IViewModel>;
                if (L_Sender != null)
                {
                    foreach (var L_SubModel in L_Sender)
                    {
                        ISubViewModel L_Sub = L_SubModel as ISubViewModel;
                        if (L_Sub != null)
                        {
                            L_Sub.Parent = this;
                        }
                    }
                }
            }
        }

        #region SubModelActivated

        public event EventHandler<ViewModelEventArgs> SubModelActivated;

        protected virtual void OnSubModelActivated(ViewModelEventArgs e)
        {
            EventHandler<ViewModelEventArgs> L_Handler = SubModelActivated;

            if (L_Handler != null)
            {
                L_Handler(this, e);
            }
        }

        #endregion SubModelActivated

        #region ActiveDocument

        private static PropertyChangedEventArgs _ActiveDocumentChangedArgs = ObservableHelper.CreateChangedArgs<ContainerViewModel>(x => x.ActiveDocument);

        private static PropertyChangingEventArgs _ActiveDocumentChangingArgs = ObservableHelper.CreateChangingArgs<ContainerViewModel>(x => x.ActiveDocument);

        private IViewModel _ActiveDocument;

        public IViewModel ActiveDocument
        {
            get
            {
                return _ActiveDocument;
            }

            set
            {
                if (value != _ActiveDocument)
                {
                    NotifyChanging(_ActiveDocumentChangingArgs);
                    _ActiveDocument = value;
                    NotifyChanged(_ActiveDocumentChangedArgs);
                }
            }
        }

        #endregion ActiveDocument

        //public override bool IsActive
        //{
        //    get
        //    {
        //        return base.IsActive || this._SubModels.Any(x => x.IsActive);
        //    }

        // set { base.IsActive = value; }
        //}
    }
}